Target :
|
Lucifer48 crackme 1 (d/l it on EB site : http://crackmes.cjb.net
or on Lucifer's 48 site : http://www.multimania.com/lucifer48 |
Protection type :
|
Serial/Name protection, logical operations |
Level :
|
Not as easy as Lucifer48 pretends :) |
Tools needed :
|
SoftIce
A Brain Some basis of cracking (recommended !!!) Maths knowledge Liters of beer A collection of punk mp3 |
Before beginning...
This crackme is based on a serial/name proctection. The serial is divided in two parts, separated by a dash '-'. The two parts are calculated in function of your name, and logical functions like XOR (mainly use although it rarely appears), OR, AND are heavily used, like ROR or SAR. Moreover, if you read the about box, you'll see that Lucifer48 qualifies his work as an "easy (logical) crackme". It's logical, that's right, but not so easy... time is money so let's go...
The essay...
There are two textbox, one for the name, the other one for the serial,
so we can use a bpx on getwindowtexta. Enter 'SiFLyiNG' in the first TextBox
and '12345' in the second one.
Then press the 'Check' button and you're back in SoftIce. Press F5
another time and F11 to return to the caller and you should land here :
:004012FB
push ebx
:004012FC
push esi
:004012FD
push edi
:004012FE
push 00000016
:00401300
push 0040223B
:00401305
push dword ptr [00402018]
* Reference To: USER32.GetWindowTextA, Ord:0000h ; this call gets the serial
:0040130B
Call 004014EC
:00401310
push 00000016
:00401312
push 00402223
:00401317
push dword ptr [00402013]
* Reference To: USER32.GetWindowTextA, Ord:0000h ; this call gets the name
:0040131D
Call 004014EC
:00401322
xor edx, edx
; edx = 0 here you should land
:00401324
xor eax, eax
; eax = 0
:00401326
xor ecx, ecx
; ecx = 0
:00401328
xor ebx, ebx
; ebx = 0
:0040132A
mov cl, 0A
; cl = Ah
:0040132C
mov esi, 0040223B ; esi = memory
location of the serial
This part of code is not really interesting. We only know that the name is at 40223B and the serial at 402223. Let's trace further and we enter in the first loop :
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401348(U)
|
:00401331
mov bl, byte ptr [esi]
; move the char of the name in bl
:00401333
test bl, bl
; if bl =00 no more char left in the serial
:00401335
je 0040147F
; jmp to bad cracker if there are no more char left
:0040133B
cmp bl, 2D
; compares bl to 2D, hexa ascii value for the dash '-'
:0040133E
je 0040134A
; if equal jump out of the loop and go on checking
:00401340
mul ecx
; these lines of
:00401342
and bl, 0F
; code
are converting the first part of the serial to
:00401345
add eax, ebx
;
hexadecimal and the result is stored in eax
:00401347
inc esi
; next char
:00401348
jmp 00401331
; loop again
Ok, it becomes interesting. This loop test if the serial is divided in two parts separated by a dash. In our case, the entered serial is '12345' so it'll jump to the bad cracker routine because it contains no dash. Besides the end of the loop converts the first part of the serial to hexadecimal and stores the result in eax. We have to change the entered serial to '12345-67890' and come back in SoftIce. There, we can jump out of the loop, as the dash is found. You can even make a '? eax' to see what eax contains : 3039. What's that ? Hey it's the first part of the serial converted to hexadecimal: 12345 = 3039h. Moreover we arrive here :
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040133E(C)
|
:0040134A push eax
; stores the first part of the code
:0040134B xor eax, eax
; eax = 0
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040135C(U)
|
:0040134D mov
bl, byte ptr [esi+01]
:00401350 test
bl, bl
:00401352 je 0040135E
; jump out of the loop and go on checking
:00401354 mul
ecx
:00401356 and bl, 0F
:00401359 add
eax, ebx
:0040135B inc esi
:0040135C jmp
0040134D
This
loop converts the second part of the serial in hexadecimal. When there
are no char left in the serial, it jumps out of the loop and goes on checking.
Make a 'd eax' and you'll see :
10932h = 67890. This is the second part of the code. Go on tracing
after the jump at 401352 and you arrive here :
:0040135E
mov edi, 00402223
; move the memory location of the name in edi. type 'd edi' and you'll
see the name
:00401363
mov cl, byte ptr [edi] ; cl =
char name
:00401365
test cl, cl
; is cl <> "" ??? <=> is name entered ?
:00401367
je 0040147F
; if not goto bad cracker routine
:0040136D
mov esi, eax
; esi = second part of code in hexa
:0040136F
mov ecx, FFFFFFFF
; ecx = -1
:00401374
xor eax, eax
; eax = 0
:00401376
repnz
:00401377
scasb
:00401378
not ecx
:0040137A
dec ecx
; the previous line get the len of the name and store it un ecx ; ecx=8
:0040137B pop
eax
; pop the first part of code in eax
:0040137C push
ecx
; store the len of the name in ecx
Then we enter in one of the most important loop of the crackme. It will transform the first part of the code.
* Referenced by a (U)nconditional or (C)onditional
Jump at Address:
|:0040138F(C)
|
:0040137D 8BD8
mov ebx, eax
; ebx= eax
:0040137F D1EB
shr ebx, 1
; ebx = SHR ebx, 1
:00401381 8BD3
mov edx, ebx
; edx = ebx = shr eax, 1
:00401383 F7D3
not ebx
; ebx = not ebx = not(SHR eax, 1)
:00401385 23D8
and ebx, eax
; ebx = ebx AND eax = not(SHR eax, 1) AND NOT(SHR eax, 1)
:00401387 F7D0
not eax
; eax = NOT eax
:00401389 23C2
and eax, edx
; eax AND edx
:0040138B 0BC3
or eax, ebx
; eax = eax OR ebx
:0040138D FEC9
dec cl
; cl = cl - 1
:0040138F 75EC
jne 0040137D
; loop until cl=0
** SHR shifts the destination right by "count" bits with zeroes shifted in on the left. (The carry flag contains the last bit shifted out), ie SHR EAX, 5 = EAX/(2^5)
Waouhhhh...
what's that ?!? It need some explanations... it seems hard but it's logical
and we are often able to simplify what logical is. So let's try. First
we should summarize what happens in this loop. We know that it depends
on cl, and cl contains the lenght of the name so it will loop cl times,
i.e. 8 times (because the lenght of 'SiFLyiNG' is 8). I'll summarize in
a table what happen at each loop with our fake code :
1st loop | 2nd loop | 3rd loop | 4th loop | 5th loop | 6th loop | 7th loop | 8th loop | |
|
|
|
|
|
|
|
|
|
mov ebx, eax | ebx = 3039h | ebx = 2825h | ebx = 3C37h | ebx = 222Ch | ebx = 333Ah | ebx = 2AA7h | ebx = 3FF4h | ebx = 200Eh |
shr ebx, 1 (= ebx/2) | ebx = 181Ch | ebx = 1412h | ebx = 1E1Bh | ebx = 1116h | ebx = 199Dh | ebx = 1553h | ebx = 1FFAh | ebx = 1007h |
and ebx, eax | ebx = 2021h | ebx = 2825h | ebx = 2024h | ebx = 2228h | ebx = 2222h | ebx = 2AA4h | ebx = 2004h | ebx = 2008h |
and eax, edx | eax = 804h | eax = 1412h | eax = 208h | eax = 1112h | eax = 885h | eax = 1550h | eax = 0Ah | eax = 1001h |
or eax, ebx | eax = 2825h | eax = 3C37h | eax = 222Ch | eax = 333Ah | eax = 2AA7h | eax = 3FF4h | eax = 200Eh | eax = 3009h |
dec cl |
|
|
|
|
|
|
|
|
eax |
|
|
|
|
|
|
|
|
Let's examine the loop and summarize what it does (we suppose that the serial is divided in two parts : X & Y so the serial is X-Y )
* 1st loop :
At line 401385 :
ebx = NOT (X/2h) AND X
then at line 40138B:
eax = NOT X AND (X/2h) OR ebx = NOT X AND (X/2) OR NOT (X/2) AND X
now use your brain : i use a picture to schematise ( i think it's more
clear) :
** a bar on X means NOT X and a bar on (X/2) means NOT (X/2)
All these lines for a XOR ;) So we can now simplify all the loops :
** some properties of the XOR function before: A XOR A = 0
(A XOR B ) XOR ( C XOR D) = A XOR B XOR C XOR D
A XOR B = B XOR A
1st loop | X XOR (X/2) |
2nd loop | (X XOR X/2h) XOR (X XOR X/2h)/2h = X XOR |
3rd loop | (X XOR X/4h) XOR (X XOR X/4h)/2h = X XOR X/4h XOR X/2h XOR X/8h |
4th loop | (X XOR X/4h XOR X/2h XOR X/8h) XOR (X XOR X/4h XOR X/2h XOR X/8h)/2
= X XOR |
5th loop | (X XOR X/10h) XOR (X XOR X/10h)/2 = X XOR X/10h XOR X/2h XOR X/20h |
6th loop | (X XOR X/10h XOR X/2h XOR X/20h) XOR (X XOR X/10h XOR X/2h XOR X/20h)/2h
= X XOR X/10h XOR |
7th loop | (X XOR X/10h XOR X/4h XOR X/40h) XOR (X XOR X/10h XOR X/4h XOR X/40h)/2h
= X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h |
8th loop | (X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h)
XOR (X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR X/20h XOR X/8h XOR X/80h)/2h
= X XOR = X XOR (X/100h) because 2h^8h = 100h = 256 |
So, all this loop is making is X XOR (X/100h) !!!
This function depends on the lenght of the entered name : a 4-lenght-name
would done : X XOR X/10h
a 7-lenght-name would done : X XOR X/10h XOR X/4h XOR X/40h XOR X/2h XOR
X/20h XOR X/8h XOR X/80h
But in my case, as len = 8, the formula is X XOR X/100h. If you don't
believe, just test all these formula with 3039h and you'll get the results
of the first table !!!
Just a verification : 3039h XOR (SHR 3039h, 8) = 3039h XOR 3039h/100h
= 3039h XOR 30h = 3009h
^^
^^
If we had entered 123456h instead of 3039h it would have done : 123456h
XOR (SHR 123456h, 8) = 123456h XOR (123456/100) = 123456h XOR 1234h
^^^^
^^^^
^^^^
or 98765432h it would have done 98765432h XOR (SHR 98765432h, 8)
= 98765432h XOR 987654h
I hope you believe the aim of this loop now...
So we can now trace and see what is after this loop :
:00401391
push eax
; stores the result of the loop
:00401392
push esi ;
stores the memory location
:00401393
mov edi, 004021FF ;
:00401398
dec edi
; edi points to "Greetings: all french crackers!"
:00401399
mov esi, 00402223 ; esi points to the name
:0040139E
mov cl, byte ptr [esi] ; CL = first char of the name
= 'S' = 53h
:004013A0 xor
eax, eax
; eax = 0
Then we enter in a new loop :
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004013AE(C)
:004013A2 mov edx, dword ptr [edi]
; edx = dword from the string "Greetings: all french crackers!"
:004013A4 add eax, edx
; eax = eax + edx
:004013A6 rol eax, cl
; ROL eax, cl = ROL eax, char from name
:004013A8 inc edi
; next char in the greetings string
:004013A9 inc esi
; next char in the name
:004013AA mov cl, byte ptr [esi]
; cl = char from name
:004013AC test cl, cl
; if cl=0 <=> no more char in name
:004013AE jne 004013A2
; stop looping
I won't explain this loop which could be useful for keygening the crackme. The most important to calculate the valid serial for 'SiFLyiNG' is to write down the result when there are no more char left in the name:
EAX = C0991D44h for 'SiFLyiNG'
:004013B0
pop ecx
; ecx = 2nd part of serial in hexa = Y
:004013B1
and eax, ecx ; eax = eax AND
eax = C0991D44h AND Y
:004013B3
mov ebx, ecx ; ebx = Y
:004013B5
shr ecx, 1
; ecx = Y/2h
:004013B7
not ecx
; ecx = NOT(Y/2h)
:004013B9
and ecx, ebx ; ecx = NOT(Y/2h)
AND Y
:004013BB
cmp eax, ecx ; compare eax and
ecx
:004013BD
jne 0040147C ; if not equal => bad
cracker
:004013C3
pop edi
; edi = 3009 = X XOR X/100h
:004013C4
pop ebx
; ebx = lenght of name
:004013C5
sub esi, ebx
; esi = memory location of the name
:004013C7
mov eax, 00000948 ; eax = 948
:004013CC
mov edx, 00001048 ; edx = 1048
Humm... another interesting part of code. This time
there is a comparison between ecx got from Y (=the second part of the serial)
and eax got from the name (=C0991D44h)
and the two registers must be equal, otherwise it jumps to the bad
cracker routine...
So what have we got ???
ecx
= NOT(Y/2h) AND Y
eax = C0991D44h AND Y
and we must have : NOT(Y/2h) AND Y = C0991D44h AND Y
<=> NOT(Y/2h) AND
Y = C0991D44 AND Y
<=> Y/2h = NOT C0991D44h
<=> Y = 3F66E2BBh * 2 = 7ECDC576h = 2127414646
Hey we have now calculated Y, i.e. the second part of the serial. Enter as serial '12345-2127414646' and come back at the comparison at 4013BB : you don't jump ;) So we can see the rest of code, another loop :
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004013DC(C)
:004013D1
mov cl, byte ptr [esi] ; ESI points to the name so cl = char
from name
:004013D3
xor cl, bl
; cl= bl (at the beginning of the loop= lenght of name) XOR cl
:004013D5
rol edx, cl
; ROL edx, cl
:004013D7
xor eax, edx
; eax = eax XOR edx
:004013D9
inc esi
; next char
:004013DA
dec bl
; is bl=0 ?
:004013DC
jne 004013D1
; if not, then loop again
We have finally at the end of this loop : EAX = 998440EAh for 'SiFLyiNG'
And if you trace another time, you arrive at the last comparison routine :
:004013DE
cmp edi, eax ; edi = 3009h
and
eax = 998440EAh
:004013E0
jne 0040147F ; if they aren't
equal go to bad cracker routine...
The end comes... what is
in EDI ??? 3009h ??? where does it come from ??? remember the loop
from 40137D to 40138F, which is only equal to a X XOR X/100h It's compared
to eax, which was calculated from the name only... so the result of the
loop from 40137D to 40138F should have been equal to 998440EAh to win the
jackpot. So we want here : edi = 998440EAh.
How to make ??? hehe... simple ;)
We know that we must have : X XOR X/100h = 998440EAh
and remember the previous examples :
3039h XOR (SHR 3039h, 8) = 3039h XOR 3039h/100h = 3039h XOR 30h
= 3009h
123456h XOR (SHR 123456h, 8) = 123456h XOR (123456/100) = 123456h
XOR 1234h
for the last examples it's like :
12 34 56
or
30 39
XOR 12 34
XOR
30
Do you follow me ??? i hope... so we should have if X = AA BB CC DD
AA BB CC DD
XOR
AA BB CC
---------------------------------
=
99 84 40
EA
so AA = 99 =
99
BB = 84
XOR AA = 84 XOR 99 = 1D
CC = 40
XOR BB = 40 XOR 1D = 5D
DD = EA
XOR CC = EA XOR 5D = B7
Hey !!! we know now AA BB CC DD, ie we know X which is the second part of code : X = 991D5DB7h = 2568838583 in decimal
SO WE KNOW NOW THE WHOLE SERIAL ;)
Name : SiFLyiNG
Serial : 2568838583-2127414646
Disable all your breakpoints and try it : "Congratulations, you did it The power of the XOR is in you! Please mail me your solution/keygen at lucifer48@yahoo.com"
The end...
Pffff... this crackme was very long to understand but if you look closer, once you have understood it's "easy". I won't make a keygen because i'm not good enough at asm, but if someone is brave enough to make it, please send me the source : i need to progress in asm... if you have questions, critics, if i have made errors or anything else, just mail me.
SiFLyiNG
siflying@ifrance.com
Greetings : Lucifer48 of course :)
Acid Burn, Eternal Bliss, Skymarshall, Carpathia, Magic Raphoun... etc...
etc